// Copyright Epic Games, Inc. All Rights Reserved.

#include "GameFeatures/LyraGameFeaturePolicy.h"

#include "AbilitySystem/LyraGameplayCueManager.h"
#include "GameFeatureData.h"
#include "GameplayCueSet.h"

/**
 * 构造函数
 */
ULyraGameFeaturePolicy::ULyraGameFeaturePolicy(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer) // 调用父类构造函数
{
}

/**
 * 获取单例实例
 */
ULyraGameFeaturePolicy& ULyraGameFeaturePolicy::Get()
{
	return UGameFeaturesSubsystem::Get().GetPolicy<ULyraGameFeaturePolicy>(); // 从游戏特性子系统获取策略
}

/**
 * 初始化游戏特性管理器
 */
void ULyraGameFeaturePolicy::InitGameFeatureManager()
{
	Observers.Add(NewObject<ULyraGameFeature_HotfixManager>()); // 添加热修复管理器观察者
	Observers.Add(NewObject<ULyraGameFeature_AddGameplayCuePaths>()); // 添加GameplayCue路径观察者

	UGameFeaturesSubsystem& Subsystem = UGameFeaturesSubsystem::Get(); // 获取游戏特性子系统
	for (UObject* Observer : Observers) // 遍历观察者
	{
		Subsystem.AddObserver(Observer); // 添加观察者
	}

	Super::InitGameFeatureManager(); // 调用父类方法
}

/**
 * 关闭游戏特性管理器
 */
void ULyraGameFeaturePolicy::ShutdownGameFeatureManager()
{
	Super::ShutdownGameFeatureManager(); // 调用父类方法

	UGameFeaturesSubsystem& Subsystem = UGameFeaturesSubsystem::Get(); // 获取游戏特性子系统
	for (UObject* Observer : Observers) // 遍历观察者
	{
		Subsystem.RemoveObserver(Observer); // 移除观察者
	}
	Observers.Empty(); // 清空观察者数组
}

/**
 * 获取游戏特性的预加载资源列表
 */
TArray<FPrimaryAssetId> ULyraGameFeaturePolicy::GetPreloadAssetListForGameFeature(const UGameFeatureData* GameFeatureToLoad, bool bIncludeLoadedAssets) const
{
	return Super::GetPreloadAssetListForGameFeature(GameFeatureToLoad, bIncludeLoadedAssets); // 调用父类方法
}

/**
 * 获取游戏特性的预加载包状态
 */
const TArray<FName> ULyraGameFeaturePolicy::GetPreloadBundleStateForGameFeature() const
{
	return Super::GetPreloadBundleStateForGameFeature(); // 调用父类方法
}

/**
 * 获取游戏特性加载模式
 */
void ULyraGameFeaturePolicy::GetGameFeatureLoadingMode(bool& bLoadClientData, bool& bLoadServerData) const
{
	// 编辑器将加载两者，这可能会导致卡顿，因为包在编辑器中设置为不预加载
	bLoadClientData = !IsRunningDedicatedServer(); // 如果不是专用服务器则加载客户端数据
	bLoadServerData = !IsRunningClientOnly(); // 如果不是仅客户端则加载服务器数据
}

/**
 * 检查插件是否允许
 */
bool ULyraGameFeaturePolicy::IsPluginAllowed(const FString& PluginURL, FString* OutReason) const
{
	return Super::IsPluginAllowed(PluginURL, OutReason); // 调用父类方法
}

//////////////////////////////////////////////////////////////////////
//

#include "Hotfix/LyraHotfixManager.h" // 包含热修复管理器

/**
 * 游戏特性加载时调用
 */
void ULyraGameFeature_HotfixManager::OnGameFeatureLoading(const UGameFeatureData* GameFeatureData, const FString& PluginURL)
{
	if (ULyraHotfixManager* HotfixManager = Cast<ULyraHotfixManager>(UOnlineHotfixManager::Get(nullptr))) // 获取热修复管理器
	{
		HotfixManager->RequestPatchAssetsFromIniFiles(); // 请求从INI文件修补资源
	}
}

//////////////////////////////////////////////////////////////////////
//

#include "AbilitySystemGlobals.h" // 包含能力系统全局
#include "GameFeatureAction_AddGameplayCuePath.h" // 包含添加GameplayCue路径的游戏特性操作

#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraGameFeaturePolicy) // 包含自动生成的代码

class FName;
struct FPrimaryAssetId;

/**
 * 游戏特性注册时调用
 */
void ULyraGameFeature_AddGameplayCuePaths::OnGameFeatureRegistering(const UGameFeatureData* GameFeatureData, const FString& PluginName, const FString& PluginURL)
{
	TRACE_CPUPROFILER_EVENT_SCOPE(ULyraGameFeature_AddGameplayCuePaths::OnGameFeatureRegistering); // CPU性能分析器事件范围
	
	const FString PluginRootPath = TEXT("/") + PluginName; // 插件根路径
	for (const UGameFeatureAction* Action : GameFeatureData->GetActions()) // 遍历游戏特性操作
	{
		if (const UGameFeatureAction_AddGameplayCuePath* AddGameplayCueGFA = Cast<UGameFeatureAction_AddGameplayCuePath>(Action)) // 检查是否是添加GameplayCue路径操作
		{
			const TArray<FDirectoryPath>& DirsToAdd = AddGameplayCueGFA->GetDirectoryPathsToAdd(); // 获取要添加的目录路径
			
			if (ULyraGameplayCueManager* GCM = ULyraGameplayCueManager::Get()) // 获取GameplayCue管理器
			{
				UGameplayCueSet* RuntimeGameplayCueSet = GCM->GetRuntimeCueSet(); // 获取运行时GameplayCue集
				const int32 PreInitializeNumCues = RuntimeGameplayCueSet ? RuntimeGameplayCueSet->GameplayCueData.Num() : 0; // 预初始化Cue数量

				for (const FDirectoryPath& Directory : DirsToAdd) // 遍历要添加的目录
				{
					FString MutablePath = Directory.Path; // 可变路径
					UGameFeaturesSubsystem::FixPluginPackagePath(MutablePath, PluginRootPath, false); // 修复插件包路径
					GCM->AddGameplayCueNotifyPath(MutablePath, /** bShouldRescanCueAssets = */ false);	// 添加GameplayCue通知路径
				}
				
				// 使用这些新路径重新构建运行时库
				if (!DirsToAdd.IsEmpty()) // 检查目录不为空
				{
					GCM->InitializeRuntimeObjectLibrary();	// 初始化运行时对象库
				}

				const int32 PostInitializeNumCues = RuntimeGameplayCueSet ? RuntimeGameplayCueSet->GameplayCueData.Num() : 0; // 后初始化Cue数量
				if (PreInitializeNumCues != PostInitializeNumCues) // 检查Cue数量是否改变
				{
					GCM->RefreshGameplayCuePrimaryAsset(); // 刷新GameplayCue主资源
				}
			}
		}
	}
}

/**
 * 游戏特性取消注册时调用
 */
void ULyraGameFeature_AddGameplayCuePaths::OnGameFeatureUnregistering(const UGameFeatureData* GameFeatureData, const FString& PluginName, const FString& PluginURL)
{
	const FString PluginRootPath = TEXT("/") + PluginName; // 插件根路径
	for (const UGameFeatureAction* Action : GameFeatureData->GetActions()) // 遍历游戏特性操作
	{
		if (const UGameFeatureAction_AddGameplayCuePath* AddGameplayCueGFA = Cast<UGameFeatureAction_AddGameplayCuePath>(Action)) // 检查是否是添加GameplayCue路径操作
		{
			const TArray<FDirectoryPath>& DirsToAdd = AddGameplayCueGFA->GetDirectoryPathsToAdd(); // 获取要添加的目录路径
			
			if (UGameplayCueManager* GCM = UAbilitySystemGlobals::Get().GetGameplayCueManager()) // 获取GameplayCue管理器
			{
				int32 NumRemoved = 0; // 移除数量
				for (const FDirectoryPath& Directory : DirsToAdd) // 遍历要添加的目录
				{
					FString MutablePath = Directory.Path; // 可变路径
					UGameFeaturesSubsystem::FixPluginPackagePath(MutablePath, PluginRootPath, false); // 修复插件包路径
					NumRemoved += GCM->RemoveGameplayCueNotifyPath(MutablePath, /** bShouldRescanCueAssets = */ false); // 移除GameplayCue通知路径
				}

				ensure(NumRemoved == DirsToAdd.Num()); // 确保移除数量等于目录数量
				
				// 仅在需要时重新构建运行时库
				if (NumRemoved > 0) // 检查有移除的目录
				{
					GCM->InitializeRuntimeObjectLibrary();	// 初始化运行时对象库
				}			
			}
		}
	}
}